package com.ganzi.manager.activiti.util;

import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ganzi.manager.activiti.base.CreateModelException;
import com.ganzi.manager.activiti.entity.*;
import org.activiti.bpmn.converter.BpmnXMLConverter;
import org.activiti.bpmn.model.*;
import org.activiti.bpmn.model.Process;
import org.activiti.editor.language.json.converter.BpmnJsonConverter;
import org.activiti.engine.history.HistoricVariableInstance;
import org.apache.commons.lang3.StringUtils;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import static com.ganzi.manager.activiti.util.ReflectUtil.createObject;
import static com.ganzi.manager.activiti.util.ReflectUtil.invokeMethod;


/**
 * /**
 * @author lxd
 * @create 2019/8/6
 * activiti中使用得到的工具方法
 */
public class ActivitiUtil {

    /**
     * 节点id 前缀（不能数字开头）
     */
    public static String NODE_IDPrefix="pp_";
    /**
     * 流程id 前缀
     */
    public static String PROCESS_IDPREFIX = "A";
    public static String START_NODE_ID = "zmdStartEvent";
    public static String END_NODE_ID = "zmdEndEvent";
    /**
     * 将历史参数列表设置到实体中去
     */
    public static <T> void setVars(T entity, List<HistoricVariableInstance> varInstanceList) {
        Class<?> tClass = entity.getClass();
        try {
            for (HistoricVariableInstance varInstance : varInstanceList) {
                Field field = tClass.getDeclaredField(varInstance.getVariableName());
                if (field == null) {
                    continue;
                }
                field.setAccessible(true);
                field.set(entity, varInstance.getValue());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 从历史参数列表中获取指定的数据
     * */
    public static String getVars(String name, List<HistoricVariableInstance> varInstanceList) {
        String result = null;
        try {
            for (HistoricVariableInstance varInstance : varInstanceList) {
                if(name.equals(varInstance.getVariableName())){
                    result = (String)varInstance.getValue();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            return result;
        }
        return result;
    }

    /***
     * ProcessFlow数组转byte
     * */
    public static byte[] ProcessFlowToByte(ProcessFlow processFlow){
        return JSONObject.toJSONBytes(processFlow);
    }

    /***
     * byte数组转ProcessFlow
     * */
    public static ProcessFlow byteToProcessFlow(byte[] bytes) throws UnsupportedEncodingException {
        String str = new String(bytes,"UTF-8");
        ProcessFlow pro = JSONObject.parseObject(str,ProcessFlow.class);
        return pro;
    }

    /**
     * 自定义的json格式转Bpmn
     * */
    public static BpmnModel converterJsonToBpmn(byte[] bytes) throws UnsupportedEncodingException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        if(bytes == null || bytes.length<=0)return null;
        ProcessFlow processFlow = byteToProcessFlow(bytes);
        return converterJsonToBpmnManyNode(processFlow);
    }

    /***
     * 自定义的json格式转Bpmn   包含多节点网关处理
     * */
    public static BpmnModel converterJsonToBpmnManyNode(ProcessFlow processFlow) {
        try{
            if(processFlow == null || processFlow.getProcessNodes() == null)return null;
            BpmnModel model = new BpmnModel();
            Process process = new Process();
            model.addProcess(process);
            process.setName(processFlow.getProcessName());
            process.setId(PROCESS_IDPREFIX + processFlow.getProcessId());
            int size = processFlow.getProcessNodes().size();
            //添加流程节点
            for (int i = 0; i < size; i++) {
                Node node = processFlow.getProcessNodes().get(i);
                String type = node.getType();
                if(StringUtils.isNotBlank(type)){
                    //创建节点
                    Object objectTemp = createObject(type.trim());
                    if(objectTemp == null){
                        return null;
                    }
                    invokeMethod(type.trim(),"setName",String.class,node.getName(),objectTemp);
                    invokeMethod(type.trim(),"setId",String.class,NODE_IDPrefix+node.getId(),objectTemp);

                    AProperties pro = node.getProperties();
                    if(pro!=null){
                        //表单
                        if(StringUtils.isNotBlank(pro.getFormKey())){
                            invokeMethod(type.trim(),"setFormKey",String.class,pro.getFormKey(),objectTemp);
                        }
                        //审批人
                        if(StringUtils.isNotBlank(pro.getAssignee())){
                            invokeMethod(type.trim(),"setAssignee",String.class,pro.getAssignee(),objectTemp);
                        }
                        //候选人
                        List<String> can = pro.getCandidateUsers();
                        if(can!=null && can.size()>0){
                            invokeMethod(type.trim(),"setCandidateUsers",List.class,can,objectTemp);
                        }
                        //候选组
                        List<String> candidateUsers = pro.getCandidateUsers();
                        if(candidateUsers!=null && candidateUsers.size()>0){
                            invokeMethod(type.trim(),"setCandidateGroups",List.class,candidateUsers,objectTemp);
                        }
                    }
                    //创建连线
                    String previous = node.getPreviousNode();
                    //创建开始节点，如果上一个节点是开始节点
                    if(START_NODE_ID.equals(previous)){
                        StartEvent satrtEvent = ProcessUtil.createStartEvent(processFlow.getFormKey());
                        SequenceFlow startSequence = ProcessUtil.createSequenceFlow(NODE_IDPrefix + START_NODE_ID, NODE_IDPrefix + node.getId());
                        List<SequenceFlow> outgoingFlows = new ArrayList<>();
                        outgoingFlows.add(startSequence);
                        satrtEvent.setOutgoingFlows(outgoingFlows);
                        process.addFlowElement(satrtEvent);
                        process.addFlowElement(startSequence);
                    }
                    //如果是网关节点
                    if(type.endsWith("Gateway")){
                        List<Condition> next = node.getNextNodes();
                        List<SequenceFlow> outgoingFlows = new ArrayList<>();
                        if(next != null && next.size()>0){
                            for(int a=0;a<next.size();a++){
                                SequenceFlow nodeSequence = ProcessUtil.createSequenceFlow(NODE_IDPrefix + node.getId(), NODE_IDPrefix + next.get(a).getNodeId());
                                //设置条件表达式
                                nodeSequence.setConditionExpression(next.get(a).getCondition());
                                process.addFlowElement(nodeSequence);
                                outgoingFlows.add(nodeSequence);
                            }
                            System.out.println("---:"+outgoingFlows.toString());
                            invokeMethod(type.trim(),"setOutgoingFlows",List.class,outgoingFlows,objectTemp);
                        }
                    }else{
                        String next = node.getNextNode();
                        SequenceFlow nodeSequence = ProcessUtil.createSequenceFlow(NODE_IDPrefix + node.getId(), NODE_IDPrefix + next);
                        process.addFlowElement(nodeSequence);
                        List<SequenceFlow> outgoingFlows = new ArrayList<>();
                        outgoingFlows.add(nodeSequence);
                        invokeMethod(type.trim(),"setOutgoingFlows",List.class,outgoingFlows,objectTemp);
                    }
                    process.addFlowElement((FlowElement)objectTemp);
                }
            }
            //创建结束节点
            process.addFlowElement(ProcessUtil.createEndEvent());
            return model;
        }catch (CreateModelException e){
            return null;
        }
    }


    /***
     * activiti的json转BpmnModel
     * */
    public static BpmnModel converterActivitiJsonToBpmn(byte[] json) throws IOException {
        JsonNode modelNode = new ObjectMapper().readTree(json);
        BpmnModel model = new BpmnJsonConverter().convertToBpmnModel(modelNode);
        return model;
    }

    /***
     * BpmnModel转activiti的json
     * */
    public static byte[] converterBpmnToActivitiJson(BpmnModel model) throws IOException {
        JsonNode mm = new BpmnJsonConverter().convertToJson(model);
        return mm.toString().getBytes("utf-8");
    }

    /***
     * 标准Bpmn的xml转BpmnModel
     * */
    public static BpmnModel converterXMLToBpmn(byte[] xml) throws XMLStreamException {
        ByteArrayInputStream bis=new ByteArrayInputStream(xml);
        BpmnXMLConverter converter=new BpmnXMLConverter();
        XMLInputFactory factory = XMLInputFactory.newInstance();
        XMLStreamReader reader = factory.createXMLStreamReader(bis);
        BpmnModel bpmnModel = converter.convertToBpmnModel(reader);
        return bpmnModel;
    }

    /***
     * BpmnModel转标准Bpmn的xml
     * */
    public static byte[] converterXmlToBpmn(BpmnModel model){
        BpmnXMLConverter bpmnXMLConverter = new BpmnXMLConverter();
        byte[] convertToXML = bpmnXMLConverter.convertToXML(model);
        return convertToXML;
    }

    /***
     * 自定义json转标准json
     * */
    public static String converterJsonToActivitiJson(String json){
        return null;
    }

    /***
     * 标准json转自定义json
     * */
    public static String converterActivitiJsonToJson(String activitiJson){
        return null;
    }
}
